package org.smart4j.framework.helper;

import com.sun.org.apache.bcel.internal.generic.RETURN;
import com.sun.org.apache.regexp.internal.RE;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.annotation.Aspect;
import org.smart4j.framework.annotation.Service;
import org.smart4j.framework.proxy.AspectProxy;
import org.smart4j.framework.proxy.Proxy;
import org.smart4j.framework.proxy.ProxyManager;
import org.smart4j.framework.proxy.TransactionProxy;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * AOP框架方法拦截助手类
 *
 * Created by chinalin on 2017/3/20.
 */
public final class AopHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger("AopHelper.class") ;
    /**
     * 初始化
     */
    static {
        try {
            Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap() ;       //获取代理类与目标类集合之间的映射关系。
            Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);       //targetMap<目标类，代理类对象List>
            for (Map.Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()){
                Class<?> targetClass = targetEntry.getKey() ;
                List<Proxy> proxyList = targetEntry.getValue() ;
                Object proxy = ProxyManager.createProxy(targetClass,proxyList) ;       //生成代理类，代理类中含proxyList，执行程序时，从proxyList中一个一个往下面链接传
                BeanHelper.setBean(targetClass,proxy);      //覆盖掉原来的类对象，改为代理对象。
            }

        }catch (Exception e){
            LOGGER.error("aop failure",e );
        }
    }

    /**
     *
     * @param aspect 类的value == Class<? extends Annotation> value()-->某一个注解类 xxx.Class ;
     * @return Aspect注解中设置的注解类的集合。
     * @throws Exception
     */
    private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception{
        Set<Class<?>> targetClassSet = new HashSet<Class<?>>() ;
        Class<? extends Annotation> annotation = aspect.value() ;
        if (annotation != null && !annotation.equals(Aspect.class)){
            targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation)) ;
        }
        return targetClassSet ;
    }

    /**
     * 获取代理类与目标类集合之间的映射关系。
     * @return Map<Class<?>,Set<Class<?>>>   KEY是代理类，value是需要这个代理的目标类。
     */
    private static Map<Class<?>,Set<Class<?>>> createProxyMap() throws Exception{
        Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>() ;
        addAspectProxy(proxyMap) ;                                                              //添加切面代理
        addTransactionProxy(proxyMap) ;                                                         //添加事务代理
        return proxyMap ;
    }
    private static void addAspectProxy(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
        Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class) ;       //代理类全部是以AspectProxy为父类的继承类。
        for (Class<?> proxyClass : proxyClassSet){                                              //遍历代理类，找出代理类的 @Aspect(value=) 例:value=org.smart4j.chapter.annotation.LtdProxy.class
            Aspect aspect = proxyClass.getAnnotation(Aspect.class) ;
            Set<Class<?>> targetClassSet = createTargetClassSet(aspect) ;
            proxyMap.put(proxyClass,targetClassSet);
        }
    }
    private static void addTransactionProxy(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
        Set<Class<?>> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class) ;       //事务代理类全部是以Service为父类的继承类。
        proxyMap.put(TransactionProxy.class,serviceClassSet);                                 //代理类中判断有@Transaction的方法就会执行一遍：事务开始、提交、回滚等操作。
    }

    /**
     * @param proxyMap<代理类，目标类Set>
     * @return targetMap<目标类，代理类对象List>
     * @throws Exception
     */
    private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception{
        Map<Class<?>,List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>() ;
        for (Map.Entry<Class<?>,Set<Class<?>>> proxyEntry : proxyMap.entrySet()){
            Class<?> proxyClass = proxyEntry.getKey();
            Set<Class<?>> targetClassSet = proxyEntry.getValue() ;
            for (Class<?> targetClass :targetClassSet){
                Proxy proxy = (Proxy) proxyClass.newInstance();
                if (targetMap.containsKey(targetClass)){
                    targetMap.get(targetClass).add(proxy) ;
                }else {
                    List<Proxy> proxyList = new ArrayList<Proxy>() ;        //List是目标类的所有代理类的集合
                    proxyList.add(proxy) ;
                    targetMap.put(targetClass,proxyList);
                }
            }
        }
        return targetMap ;
    }

}
